package com.linkgie.galaxyframework.utils.bean;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import com.linkgie.galaxyframework.utils.lang.DataTypeUtils;

/**
 * Bean对象的字段选择器；
 * <p>
 * 定义了实体的属性表和属性选择操作；
 */
public class BeanFieldSelector<T> {


    private static final Object MUTEX = new Object();

    private static final Map<Class<?>, BeanFieldSelector<?>> PROPERTY_SELECTORS = new ConcurrentHashMap<>();

    @SuppressWarnings("unchecked")
    public static <T> BeanFieldSelector<T> resolve(Class<T> entityType){
        BeanFieldSelector<T> entityFieldSelector = (BeanFieldSelector<T>) PROPERTY_SELECTORS.get(entityType);
        if (entityFieldSelector == null) {
            synchronized(MUTEX){
                entityFieldSelector = (BeanFieldSelector<T>) PROPERTY_SELECTORS.get(entityType);
                if (entityFieldSelector == null) {
                    entityFieldSelector = new BeanFieldSelector<T>(entityType);
                    PROPERTY_SELECTORS.put(entityType, entityFieldSelector);
                }
            }
        }
        return entityFieldSelector;
    }


    //--------------------------------------------


    private BeanMetadata metadata;

    private Class<T> beanType;

    /**
     * 实体代理对象；
     * <p>
     */
    private T entityProxy;

    private ThreadLocal<String> fieldNameHolder = new ThreadLocal<>();

    /**
     * @return the entityType
     */
    public Class<T> getType() {
        return beanType;
    }

    public BeanMetadata getMetadata() {
        return metadata;
    }

    @SuppressWarnings("unchecked")
    private BeanFieldSelector(Class<T> beanType) {
        this.beanType = beanType;
        this.metadata = BeanMetadata.resolve(beanType);
        this.entityProxy = (T)createSelectorProxy(beanType, this.metadata);
    }

    private Object createSelectorProxy(Class<?> beanType, BeanMetadata metadata) {
        FieldSelectInterceptor interceptor = new FieldSelectInterceptor(metadata);

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(beanType);
        enhancer.setCallback(interceptor);

        return enhancer.create();
    }

    /**
     * 选择一项属性；
     * 
     * @param selectFunction
     * @return
     */
    public String select(FieldSelect<T> selectFunction) {
        try {
            // if (fieldNameHolder.get() != null) {
            //     // 已经选择过属性；
            //     throw new IllegalStateException("The property selection is ready! Cannot select property twice!");
            // }

            selectFunction.select(entityProxy);

            String propertyName = fieldNameHolder.get();
            if (propertyName == null) {
                throw new IllegalStateException(
                        "No property of entity type[" + metadata.getBeanType().getName() + "] is selected!");
            }
            return propertyName;
        } finally {
            // 调用后确保清空选择器；
            fieldNameHolder.remove();
        }
    }

    /**
     * 属性选择拦截处理；
     * <p>
     * 根据代理对象的方法调用，确定选择的属性；
     */
    private class FieldSelectInterceptor implements MethodInterceptor {

        private final BeanMetadata beanMetadata;

        /**
         * 字段的代理；
         * 
         * key : 字段名；
         * value: 字段对应的代理对象；
         */
        private Map<String, Optional<?>> subEntityProxies = new ConcurrentHashMap<>();

        FieldSelectInterceptor(BeanMetadata beanMetadata){
            this.beanMetadata = beanMetadata;
        }

        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
           // 拦截，并记录选择路径；
            String propertyName = this.beanMetadata.getFieldName(method);
            if (propertyName == null) {
                // 不是属性选择器方法；
                throw new UnsupportedOperationException("The invoked method[" + method.getName()
                        + "] is not a property selector method which is represented as a getter of POJO!");
            }

            String propertyPath;

            String parentPropertyName = fieldNameHolder.get();
            if (parentPropertyName != null) {
                // 生成嵌套的多级属性路径；
                propertyPath = String.format("%s.%s", parentPropertyName, propertyName);
            }else{
                propertyPath = propertyName;
            }
            fieldNameHolder.set(propertyPath);


            // 代理对象总是返回类型的默认值；
            Class<?> returnType = method.getReturnType();
            boolean isPrimitive = DataTypeUtils.isPrimitiveType(returnType);
            if (isPrimitive) {
                Object defaultValue = DataTypeUtils.getDefaultValue(returnType);
                return defaultValue;
            }
            if (returnType.isEnum()) {
                return null;
            }
            if (returnType.isArray()) {
                return null;
            }

            // 检查是否是已经解析过的嵌套实体类型；
            Optional<?> subSelectorProxyOptional =  subEntityProxies.get(propertyName);
            if (subSelectorProxyOptional != null) {
                return subSelectorProxyOptional.orElse(null);
            }
            synchronized(this){
                subSelectorProxyOptional =  subEntityProxies.get(propertyName);
                if (subSelectorProxyOptional != null) {
                    return subSelectorProxyOptional.orElse(null);
                }
                // 如果返回的类型是 POJO 类型，则创建字段嵌套的代理对象；
                if (returnType != Void.class 
                    && returnType != Object.class 
                    && returnType != String.class 
                    && !returnType.getName().startsWith("java.")
                    && !returnType.getName().startsWith("javax.")
                    && !returnType.getName().startsWith("org.springframework.")) {
                    
                    BeanMetadata subEntityMetadata = BeanMetadata.resolve(returnType);
                    Object subSelectorProxy = createSelectorProxy(returnType, subEntityMetadata);
                    subSelectorProxyOptional = Optional.of(subSelectorProxy);

                }else{
                    subSelectorProxyOptional = Optional.empty();
                }

                subEntityProxies.put(propertyName, subSelectorProxyOptional);
            }

            return subSelectorProxyOptional.orElse(null);
        }

    }

}
